package inutan.gateway.session.user;

import inutan.gateway.external.GamePack;
import org.ricks.common.actor.Actor;
import org.ricks.common.lang.Assert;
import org.ricks.common.lang.Logger;
import org.ricks.common.utils.TimeUtils;
import org.ricks.net.AioSession;
import org.ricks.net.buffer.WriteBuffer;
import org.ricks.net.transport.kcp.KcpSession;
import org.ricks.net.transport.kcp.Ukcp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Objects;

/**
 * 用户的唯一 session 信息
 * <pre>
 *     与 channel 是 1:1 的关系，可取到对应的 userId、channel 等信息
 *
 */
public class UserSession {

    /** user channel  要么连上kcp 要么连上tcp       */
    private final AioSession aioSession;
    /** userId */
    private long userId;
    /** 用户状态 */
    private UserSessionState state;
    /**
     * 业务节点ID ,如果分配好了 直至user session 销毁,才会解绑 避免动态增删节点 路由问题
     * 如果已经绑定的节点 session 管理中心未拿到节点，说明节点被删除，重新分配节点
     *  模块绑定节点
     *
     *  规则分配的内部session
     *  玩家退出5分钟心跳超时 自动销毁UserSession ,内部session绑定关系自然解绑
     */
    private AioSession[] logicSessions = new AioSession[Byte.MAX_VALUE];

    /**
     * key=房间类型
     * value=groupId 分组ID
     * 维护房间绑定关系，每次请求传给flowcontext
     * 朝生夕死，社团聊天属于社团功能
     */
    private HashMap<Byte,String> roomMap = new HashMap<>();

    private boolean loginSuccess;

    public UserSession(AioSession session) {
        this.aioSession = session;
        this.state = UserSessionState.ACTIVE;
    }

    /**
     * 设置当前用户（玩家）的 id
     * <pre>
     *     当设置好玩家 id ，也表示着已经身份验证了（表示登录过了）。
     * </pre>
     *
     * @param userId userId
     */
    public void setUserId(long userId) {
        this.userId = userId;
    }

    public long getUserChannelId() {
        return aioSession.sid();
    }

    /**
     * moduleId 获得绑定节点
     * @param moduleId
     * @return
     */
    public AioSession getLogicSession(byte moduleId) {
        return logicSessions[moduleId];
    }

    public void addLogicSession(byte moduleId,AioSession session) {
        logicSessions[moduleId] =  session;
        Logger.info("玩家-模块-session绑定 userId:["+userId+"] moduleId[" + moduleId + "] join session:" + session);
    }

    public void removeLogicSession(byte moduleId) {
        logicSessions[moduleId] = null;
    }


    /**
     * 获取玩家ip
     *
     * @return 获取玩家ip
     */
    public String getIp() {
        if (Boolean.FALSE.equals(isActiveChannel())) {
            return "";
        }

        InetSocketAddress inetSocketAddress;
        try {
            inetSocketAddress = aioSession.getRemoteAddress();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return inetSocketAddress.getHostString();
    }


    /**
     * 是否进行身份验证
     *
     * @return true 已经身份验证了，表示登录过了。
     */
    public boolean isVerifyIdentity() {
        return this.userId > 0 && loginSuccess;
    }

    public boolean isActiveChannel() {
        return Objects.nonNull(aioSession) && !aioSession.isInvalid();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }

        if (!(o instanceof UserSession that)) {
            return false;
        }

        return userId == that.userId;
    }

    @Override
    public int hashCode() {
        return Objects.hash(userId);
    }

    private String getChannelId() {
        return String.valueOf(this.aioSession.sid());
    }

    public void setState(UserSessionState state) {
        this.state = state;
    }

    public void close() {
        if(aioSession != null) aioSession.close();
    }

    public void writeAndFlush(GamePack msg) {
            if(aioSession == null || aioSession.isInvalid()) {
                Logger.info(".................");
                return;
            }
            if(aioSession instanceof KcpSession kcpSession) {
                int headLength = 2 + 4 + 2;
                //游戏默认是小端
                ByteBuffer byteBuffer = ByteBuffer.allocate(headLength + msg.packet().length);
                byteBuffer.putShort(msg.cmd());
                byteBuffer.putInt(msg.getPackageId());
                byteBuffer.putShort(msg.responseStatus);
                byteBuffer.put(msg.packet());
                byteBuffer.flip();//重置 limit 和postion 值 否则 buffer 读取数据不对
                byte[] bytes = new byte[headLength + msg.packet().length];
                byteBuffer.get(bytes);
//            System.err.println("发送 字节长度：" + bytes.length + " & " + bytes[0] + "," + bytes[1]);
                aioSession.send(bytes);
//                    if (code != 0) close();
            } else writeAndFlushTcp(msg);
    }

    private void writeAndFlushTcp(GamePack msg) {
        WriteBuffer buffer = aioSession.writeBuffer();
        try {
            System.err.println(msg.packet().length + 2 + 4 + 2);
            buffer.writeInt(msg.packet().length + 2 + 4 + 2);
            buffer.writeShort(msg.cmd());
            buffer.writeInt(msg.getPackageId());
            buffer.writeShort(msg.responseStatus);
            buffer.write(msg.packet());
        } catch (Exception e) {
            try {
                Logger.error("cmd:"+msg.cmd()+" 写回客户端:"+aioSession.getRemoteAddress()+" 失败.");
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        } finally {
            buffer.flush();
        }
    }

    public long getUserId() {
        return userId;
    }

    public UserSessionState getState() {
        return state;
    }

    public AioSession getAioSession() {
        return aioSession;
    }

    public boolean isLoginSuccess() {
        return loginSuccess;
    }

    public void setLoginSuccess(boolean loginSuccess) {
        this.loginSuccess = loginSuccess;
    }

    public HashMap<Byte, String> getRoomMap() {
        return roomMap;
    }

    public void setRoomMap(HashMap<Byte, String> roomMap) {
        this.roomMap = roomMap;
    }
}
